package org.tool.reflect;

import cn.hutool.core.bean.BeanUtil;
import org.tool.TipException;
import org.tool.collection.ArrayUtil;
import org.tool.core.lang.Assert;
import org.tool.reflect.interfaces.ReflectGenerate;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Objects;

import static org.tool.reflect.ReflectUtil.ReflectConstant.*;

/**
 * 反射 相关工具类
 *
 * @author 李坤
 * @date 2022/4/18 9:09
 */
public abstract class ReflectUtil {

    /**
     * 一个 {@link Field} 类型的空数组
     */
    public static final Field[] EMPTY_FIELD_ARRAY = new Field[]{};

    /**
     * 为给定的对象的字段设置值
     *
     * @param source     给定的对象
     * @param field      要赋值的字段
     * @param fieldValue 要赋值的值
     */
    public static void setFieldValue(Object source, Field field, Object fieldValue) {
        Assert.notNull(source, "source 不能为空");
        Assert.notNull(field, "field 不能为空");

        try {
            field.setAccessible(true);
            field.set(source, fieldValue);
        } catch (Exception exception) {
            throw new TipException(String.format("为类 %s 的字段 %s 设置值失败", source.getClass(), field));
        }
    }

    /**
     * 为给定的对象的字段设置值
     *
     * @param source     给定的对象
     * @param filedName  要赋值的字段
     * @param fieldValue 要赋值的值
     */
    public static void setFieldValue(Object source, String filedName, Object fieldValue) {
        Assert.notNull(source, "source 不能为空");
        Assert.hasText(filedName, "filedName 不能为空");

        final Class<?> clazz = source.getClass();
        try {
            final Field field = clazz.getDeclaredField(filedName);

            field.setAccessible(true);
            field.set(source, fieldValue);
        } catch (NoSuchFieldException exception) {
            throw new TipException(String.format("%s 类中不存在 %s 字段", clazz, filedName));
        } catch (IllegalAccessException exception) {
            throw new TipException(String.format("为类 %s 的字段 %s 设置值失败", clazz, filedName));
        }
    }

    /**
     * 确定给定的方法是否是 "equals" 方法。
     *
     * @see Object#equals(Object)
     */
    public static boolean isEqualsMethod(Method method) {
        if (method == null) {
            return false;
        }
        if (method.getParameterCount() != 1) {
            return false;
        }
        if (!method.getName().equals(METHOD_EQUALS)) {
            return false;
        }
        return method.getParameterTypes()[0] == Object.class;
    }

    /**
     * 确定给定的方法是否是 "hashCode" 方法。
     *
     * @see Object#hashCode()
     */
    public static boolean isHashCodeMethod(Method method) {
        return method != null && method.getParameterCount() == 0 && method.getName().equals(METHOD_HASH_CODE);
    }

    /**
     * 确定给定的方法是否是 "toString" 方法。
     *
     * @see Object#toString()
     */
    public static boolean isToStringMethod(Method method) {
        return (method != null && method.getParameterCount() == 0 && method.getName().equals(METHOD_TO_STRING));
    }

    /**
     * 确定给定方法是否最初由 {@link Object} 声明。
     */
    public static boolean isObjectMethod(Method method) {
        return (method != null && (method.getDeclaringClass() == Object.class || isEqualsMethod(method) || isHashCodeMethod(method) || isToStringMethod(method)));
    }

    /**
     * 获取指定类和其父类的Field对象
     *
     * @param clazz 目标类
     *
     * @return Field对象
     */
    public static Field[] fieldsWithSuper(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        return ReflectUtil.hasSuperClass4Field(clazz) ? ArrayUtil.merge(fields, ReflectUtil.fieldsWithSuper(clazz.getSuperclass())) : fields;
    }


    /**
     * 根据给定的 tClass.公共构造函数 生成对象，并将 source 中的属性赋值给 该对象
     *
     * @param tClass 要生成的对象的类型
     * @param source 属性源
     * @param <T>    泛型
     *
     * @return 生成并赋值的对象
     */
    public static <T> T generateTargetAndCopyProperties(Class<T> tClass, Object source) {
        Assert.notNull(tClass, "Class对象和数据源不能为空");
        Assert.notNull(source, "Class对象和数据源不能为空");

        final T target = ConstructorUtil.newInstance(tClass);
        // 填充属性数据
        BeanUtil.copyProperties(source, target);

        // 调用接口，重新设置部分属性值
        if (target instanceof ReflectGenerate) {
            ((ReflectGenerate) target).execute(source);
        }
        return target;
    }


    /*--------------------------------------------------private methods-----------------------------------------------*/


    /**
     * 判断给定的类是否有非 Object 类型的父类
     *
     * @param clazz 目标类
     *
     * @return 是否有非 Object 类型的父类
     */
    private static boolean hasSuperClass4Field(Class<?> clazz) {
        Class<?> superclass = clazz.getSuperclass();
        return Objects.nonNull(superclass) && !Object.class.equals(superclass);
    }


    /*----------------------------------------------private static class----------------------------------------------*/


    public interface ReflectConstant {

        /**
         * equals 方法名
         */
        String METHOD_EQUALS = "equals";

        /**
         * toString 方法名
         */
        String METHOD_FINALIZE = "finalize";

        /**
         * hashCode 方法名
         */
        String METHOD_HASH_CODE = "hashCode";

        /**
         * toString 方法名
         */
        String METHOD_TO_STRING = "toString";
    }

}
